package com.yc.exam.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc.exam.entity.User;
import com.yc.exam.exception.BusinessException;
import com.yc.exam.service.IRoleService;
import com.yc.exam.service.IUserService;
import com.yc.exam.vo.MessageVo;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.List;

/**
 * SpringSecurity的配置
 *
 * @author 75073
 */
@Configuration
@EnableWebSecurity
@AllArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  private final IUserService userService;
  private final IRoleService roleService;
  private final PasswordEncoder passwordEncoder;

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity
        .csrf()
        .disable()
        .authorizeRequests()
        // 允许对于网站静态资源的无授权访问
        .antMatchers(
            HttpMethod.GET,
            "/favicon.ico",
            "/**/*.css",
            "/swagger-resources/**",
            "/v2/api-docs/**",
            "webjars/**",
            "/**/*.js",
            "/**/*.jpg")
        .permitAll()
        // 对登录注册要允许匿名访问
        .antMatchers("/index/login", "/index/register", "/exam/user/registered")
        .permitAll()
        .antMatchers(HttpMethod.GET, "/exam/role")
        // 跨域请求会先进行一次options请求
        .permitAll()
        .antMatchers(HttpMethod.OPTIONS)
        .permitAll()
        .anyRequest() // 除上面外的所有请求全部需要鉴权认证
        .authenticated();
    httpSecurity
        .formLogin()
        .loginPage("/index/login")
        .loginProcessingUrl("/login")
        .failureHandler(
            (httpServletRequest, httpServletResponse, e) -> {
              httpServletResponse.setCharacterEncoding("UTF-8");
              httpServletResponse.setContentType("application/json");
              ObjectMapper mapper = new ObjectMapper();
              MessageVo messageVo = new MessageVo(e.getLocalizedMessage());
              httpServletResponse.getWriter().print(mapper.writeValueAsString(messageVo));
              httpServletResponse.setStatus(500);
            })
        .successHandler(
            (httpServletRequest, httpServletResponse, authentication) -> {
              httpServletResponse.setCharacterEncoding("UTF-8");
              httpServletResponse.setContentType("application/json");
              ObjectMapper mapper = new ObjectMapper();
              MessageVo messageVo = new MessageVo("登录成功！！");
              httpServletResponse.getWriter().print(mapper.writeValueAsString(messageVo));
              httpServletResponse.setStatus(200);
            })
        .and()
        .logout()
        .invalidateHttpSession(true)
        .deleteCookies("JSESSIONID");
    // 禁用缓存
    httpSecurity.headers().cacheControl();
    // 添加自定义未授权和未登录结果返回
    httpSecurity.headers().frameOptions().disable();
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
  }

  @Override
  @Bean
  public UserDetailsService userDetailsService() {
    // 获取登录用户信息
    return username -> {
      // 获取用户名
      User user = userService.loadUserByUserName(username);
      if (user != null) {
        // 获取相关权限
        List<String> roles = roleService.loadUserRoleByUserId(user.getId());
        return org.springframework.security.core.userdetails.User.builder()
            .username(user.getUsername())
            .password(user.getPassword())
            .roles(roles.toArray(new String[] {}))
            .build();
      }
      throw new BusinessException("用户名或密码错误");
    };
  }
}
